//
// Created by wangwanlong on 2024/11/1.
//

#ifndef MKAHYPAR_PARTITIONER_FACADE_H
#define MKAHYPAR_PARTITIONER_FACADE_H
#include "mkahypar/utils/typedef.h"
#include "mkahypar/partition/context.h"
#include "mkahypar/utils/timer.h"
#include "mkahypar/partition/partitioner.h"
#include "mkahypar/partition/preprocessing/louvain.h"
#include "mkahypar/partition/preprocessing/single_node_hyperedge_remover.h"
namespace mkahypar{
  class PartitionerFacade{
  public:
    void partition(Hypergraph& hg,TargetGraph & topo){

      //设置运行时间,开启计时
      Timer::getInstance().setTime(Context::getInstance().run_time);
      performTimeLimitedRepeatedPartitioning(hg,topo);

    }
  private:
    //预处理
    void preProcess(Hypergraph& hg){
      //删除单点边
      auto result = _singleNodeHyperedgeRemover.removeSingleNodeHyperedges(hg);
      LOG<<"remove singel edges:"<<result.num_removed_single_node_hes;
      //初始化所有超边的tar 初始值
      for(const HyperedgeID& he : hg.edges()){
        HypernodeID source = hg.hyperedge(he).source();
        for(const auto& tar : hg.pins(he)){
          if(tar == source)
            continue;
          hg.edgeTarRouteCount(he)[tar] = 1;
        }
      }
      //社区识别
      const double density = static_cast<double>(hg.initialNumEdges()) /
                             static_cast<double>(hg.initialNumNodes());
      if(density < 0.75)
        Context::getInstance().community_detection_edge_weight = LouvainEdgeWeight::degree;
      else
        Context::getInstance().community_detection_edge_weight = LouvainEdgeWeight::uniform;


      detectCommunities(hg);


    }

    void postProcess(Hypergraph& hg){
      //恢复被删除的单点边
      _singleNodeHyperedgeRemover.restoreSingleNodeHyperedges(hg);
    }
    //验证解
    void verifyResult(Hypergraph& hg, TargetGraph&topo){
      //验证资源是否超载
      int num_partitions = hg._k;
      std::vector<NodeWeight>use_res(num_partitions);
      for(auto hn : hg.nodes()){
        PartitionID id = hg.partID(hn);
        use_res[id] += hg.nodeWeight(hn);
      }
      for(PartitionID id = 0; id < num_partitions;++id){
        for(int j=0;j<use_res[id].size();++j){
          if(topo._fpga[id]._bound_res[j] < use_res[id][j]) {
            ERROREXIT("res error!");
          }
        }
        use_res[id].print();
      }
      //验证路由和超边
      int64_t hop_len = 0;
      std::vector<int64_t>use_line(num_partitions);
      for(auto he : hg.edges()){
        int connect = 0;
        auto source_part = hg.partID(hg.hyperedge(he).source());
        auto he_weight = hg.edgeWeight(he);
        for(auto hn : hg.pins(he)){
          PartitionID id = hg.partID(hn);
          connect |= 1LL << id;
          if(hn != hg.hyperedge(he).source()){
            int dist = topo._distance[source_part][hg.partID(hn)];
            if(dist > Context::getInstance().hop_limit){
              ERROREXIT("hop error!");
            }
//            if(he_weight && dist){
//              LOG<<"he_weight:"<<he_weight<<Context::getInstance().id2nodeName[hg.hyperedge(he).source()]
//              <<"to"<<Context::getInstance().id2nodeName[hn]<< "-----"<<source_part<<hg.partID(hn);
//            }
//            hop_len += he_weight * dist;

          }
        }
        int count = __builtin_popcountll(connect);
        if(count > 1){
          //边界边
          for(PartitionID id = 0; id < num_partitions;++id){
            if((connect >> id) & 1){
              use_line[id] += he_weight;
              hop_len += he_weight * topo._distance[source_part][id];

            }
          }
        }
      }
      for(PartitionID id = 0; id < num_partitions;++id){
        LOG<<"id use line:"<<use_line[id];
        if(topo._fpga[id]._bound_line < use_line[id]){
          ERROREXIT("line error!");
        }
      }
      LOG<<"vefify success!";
      LOG<<"total hop len = "<<hop_len;
    }
    //执行V循环划分
    void performTimeLimitedRepeatedPartitioning(Hypergraph& hg, TargetGraph&topo){
      Partitioner partitioner(hg,topo);
      //设置缩合参数
      partitioner.setupContext();
      //预处理路径
      partitioner.calDistance();
      preProcess(hg);
      std::vector<HypernodeID>result(hg.initialNumNodes(),-1);
      Metrics best;
      best.obj = std::numeric_limits<GainType>::max();
      int iter = 0;
      while(!Timer::getInstance().TLE() && iter < Context::getInstance().max_iter ){

        auto current = partitioner.partition();

        //计算obj
        if(current.obj < best.obj){

          best = current;
          LOG<<"iter:"<<iter<<"cost time:"<<Timer::getInstance().getCostTime()<<"best obj:";
          best.print();
          for(auto hn : hg.nodes()){
            result[hn] = hg.partID(hn);
          }
        }
        hg.reset();
        topo.reset();
        ++iter;
      }

      //还原为历史最优解
      for(auto hn : hg.nodes()){
        hg.hypernode(hn).part_id = result[hn];
      }

      //检查这个解是否合法
      verifyResult(hg,topo);

    }


    SingleNodeHyperedgeRemover _singleNodeHyperedgeRemover;//预处理删除单点边

  };
}
#endif //MKAHYPAR_PARTITIONER_FACADE_H
